﻿using System;
using System.Threading.Tasks;
using Proto;
using Proto.TestFixtures;
using Proto.TestKit;
using Xunit;

namespace Proto.Tests;

public class WatchTests
{
    [Fact]
    public async Task MultipleStopsTriggerSingleTerminated()
    {
        await using var system = new ActorSystem();
        var context = system.Root;

        var (probe, probePid) = system.CreateTestProbe();

        // child stops itself twice when receiving "stop"
        var childProps = Props.FromFunc(ctx =>
        {
            if (ctx.Message is "stop")
            {
                ctx.Stop(ctx.Self);
                ctx.Stop(ctx.Self);
            }

            return Task.CompletedTask;
        });

        var child = context.Spawn(childProps);

        // register probe as watcher for the child
        child.SendSystemMessage(system, new Watch(probePid));

        context.Send(child, "stop");

        await probe.ExpectNextSystemMessageAsync<Terminated>(t => Equals(t.Who, child));
        // ensure no additional messages are left in the probe
        await probe.ExpectEmptyMailboxAsync();
    }

    [Fact]
    public async Task CanWatchLocalActors()
    {
        await using var system = new ActorSystem();
        var context = system.Root;

        var watchee = context.Spawn(Props.FromProducer(() => new DoNothingActor()));

        var (probe, probePid) = system.CreateTestProbe();

        watchee.SendSystemMessage(system, new Watch(probePid));

        await context.StopAsync(watchee);

        await probe.ExpectNextSystemMessageAsync<Terminated>(t => Equals(t.Who, watchee));
    }

    [Fact]
    public async Task UnwatchPreventsTerminatedMessage()
    {
        await using var system = new ActorSystem();
        var context = system.Root;

        var watchee = context.Spawn(Props.FromProducer(() => new DoNothingActor()));

        var (probe, probePid) = system.CreateTestProbe();

        watchee.SendSystemMessage(system, new Watch(probePid));
        watchee.SendSystemMessage(system, new Unwatch(probePid));

        await context.StopAsync(watchee);

        await probe.ExpectNoMessageAsync(TimeSpan.FromMilliseconds(100));
    }
}